#include <windows.h>
#include <stdio.h>
#include <ShlObj.h>
#include <strsafe.h>
#include "ntdll.h"
#include "md5.h"
#include "nt_status.h"

#pragma comment(lib, "ntdll.lib")
#pragma comment(lib, "advapip.lib")


// Structure definitions
typedef struct _KEY_VALUE_BASIC_INFORMATION {
	ULONG TitleIndex;
	ULONG Type;
	ULONG NameLength;
	WCHAR Name[1];
} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;

typedef struct _FILE_FS_VOLUME_INFORMATION {
	LARGE_INTEGER VolumeCreationTime;
	ULONG VolumeSerialNumber;
	ULONG VolumeLabelLength;
	BOOLEAN SupportsObjects;
	WCHAR VolumeLabel[1];
} FILE_FS_VOLUME_INFORMATION, *PFILE_FS_VOLUME_INFORMATION;

BYTE g_CLSID[32] = {0};
CHAR g_CLSIDPATH[100] = {0};

VOID ZARemoveServiceStartEntry()
{
	WCHAR *ServicesPath = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\";
	BYTE wParameters[] = {'P', 0, 'a', 0, 'r', 0, 'a', 0, 'm', 0, 'e', 0, 't', 0, 'e', 0, 'r', 0, 's', 0, 0, 0, 0x2e, 0x20, 0x64, 0x27, 0, 0};
	//WCHAR wParameters[] = {'P', 'a', 'r', 'a', 'm', 'e', 't', 'e', 'r', 's', '.', 'd', '\'', 0};
	BYTE KeyName[] = {0x2E, 0x20, 0x65, 0x00, 0x74, 0x00, 0x61, 0x00, 0x64, 0x00, 0x70, 0x00, 0x75, 0x00, 0x67, 0x00, 0x00, 0x00}; //<-[0][1] Unicode LRO
	BYTE Key[MAX_PATH];
	UNICODE_STRING usTmp, usParams;
	OBJECT_ATTRIBUTES obja;
	NTSTATUS Status;
	HANDLE hKey, hSubKey;
	ULONG bytesIO = 0;
	BOOL bRTOSubKey = FALSE;

	wprintf(L"Removing Service startup entry\n");
	wcscpy((PWSTR)&Key[0], ServicesPath);
	wcscat((PWSTR)&Key, (PWSTR)&KeyName);
	RtlInitUnicodeString(&usTmp, (PCWSTR)&Key);
	InitializeObjectAttributes(&obja, &usTmp, OBJ_CASE_INSENSITIVE, NULL, NULL);

	Status = NtOpenKey(&hKey, KEY_ALL_ACCESS, &obja);
	if (NT_SUCCESS(Status)) {

		// Enumerate its sub-keys
		PKEY_BASIC_INFORMATION pSubKeyName;
		DWORD dwRequestLength;
		DWORD dwLength = 100;
		DWORD dwKeyindex = 0;

		pSubKeyName = (PKEY_BASIC_INFORMATION)malloc(dwLength);
		do{
			memset(pSubKeyName, 0, dwLength);

			// Enumerate all the registry key's values
			Status = ZwEnumerateKey(hKey, dwKeyindex, KeyBasicInformation, pSubKeyName, dwLength, &dwRequestLength);

			// Find targeted Parameters key
			if (pSubKeyName->NameLength > 0)
			{
				int index;
				WCHAR *wSubKeyName = pSubKeyName->Name;
				for (index=0; index < 28; index++)
					if (wParameters[index] != *((BYTE*)wSubKeyName+index))
						break;

				if (index == 28)
					bRTOSubKey = TRUE;
			}
			dwKeyindex++;

		}while(Status != STATUS_NO_MORE_ENTRIES && !bRTOSubKey);

		// If targeted RTO key found, remove the key
		if (bRTOSubKey)
		{
			usParams.Buffer = (PWSTR)wParameters;
			//usParams.Length = wcslen(wParameters) * sizeof(WCHAR);
			usParams.Length = 13 * sizeof(WCHAR);	// The size if VERY important!!!
			usParams.MaximumLength = usParams.Length + sizeof(WCHAR);
			obja.RootDirectory = hKey;
			obja.ObjectName = &usParams;
			Status = NtOpenKey(&hSubKey, KEY_ALL_ACCESS, &obja);
			if (NT_SUCCESS(Status)) { 
				NtDeleteKey(hSubKey);
				NtClose(hSubKey);
				wprintf(L"[+] Service key \"%wZ\" removed successfully!\n", &usParams);
			}
			else
				wprintf(L"[-] Failed to remove service key \"%wZ\"(0x%08x)!\n", &usParams, GetLastError());

			if (NT_SUCCESS(NtDeleteKey(hKey)))
				wprintf(L"[+] Service key \"%wZ\" removed successfully!\n", &usTmp);
			else
				wprintf(L"[-] Failed to remove service key \"%wZ\"(0x%08x)!\n", &usTmp, GetLastError());
		}
		
		NtClose(hKey);
	}
	else
		wprintf(L"[-] Failed to open Services key (0x%08x)\n", Status);

}

// Delete run key using Win32 API
VOID ZARemoteRunStartupKeyWithWin32()
{
	HKEY hKey;
	BYTE wszGoogleUpdateValueName[] = {'G', 0, 'o', 0, 'o', 0, 'g', 0, 'l', 0, 'e', 0, ' ', 0, 
										'U', 0, 'p', 0, 'd', 0, 'a', 0, 't', 0, 'e', 0, 0, 0,
										'\x2e', '\x20', '\x64', '\x27', 0, 0}; 

	RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_ALL_ACCESS, &hKey);
	RegDeleteValue(hKey, (LPWSTR)&wszGoogleUpdateValueName);
	RegCloseKey(hKey);

}

BOOL ZARemoveRunStartupKeyWithNative(LPWSTR wValueName)
{
	PKEY_VALUE_BASIC_INFORMATION pKeyValueData;
	OBJECT_ATTRIBUTES	oaKeyName;
	OBJECT_ATTRIBUTES	oaValueName;
	UNICODE_STRING		usValueName;
	UNICODE_STRING		usUserKeyName;
	UNICODE_STRING		usFullUserKeyName;
	WCHAR				wszFullUserKeyName[MAX_PATH] = {0};
	WCHAR				*wszRunValueData;
	NTSTATUS			status;
	HANDLE				hKey;
	ULONG				ulSize;

	status = RtlFormatCurrentUserKeyPath(&usUserKeyName);
	if (!NT_SUCCESS(status))
		return FALSE;

	swprintf(&wszFullUserKeyName[0], MAX_PATH, L"%ws\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", usUserKeyName.Buffer);

	RtlInitUnicodeString(&usValueName, wValueName);
	RtlInitUnicodeString(&usFullUserKeyName, &wszFullUserKeyName[0]);

	InitializeObjectAttributes(&oaValueName, &usValueName, OBJ_CASE_INSENSITIVE, NULL, NULL );
	InitializeObjectAttributes(&oaKeyName, &usFullUserKeyName, OBJ_CASE_INSENSITIVE, NULL, NULL );

	status = ZwCreateKey(&hKey, KEY_ALL_ACCESS, &oaKeyName, 0, NULL, 0, NULL);
	if (!NT_SUCCESS(status))
		return FALSE;
	{
		DWORD dwRequestLength;
		DWORD dwLength = 1000;
		DWORD dwKeyindex = 0;

		pKeyValueData = (PKEY_VALUE_BASIC_INFORMATION)malloc(dwLength);
		do{
			memset(pKeyValueData, 0, dwLength);

			// Query the specific registry key's value directly
			//status = ZwQueryValueKey(hKey, &usValueName, KeyFullInformation, pKeyValueData, dwRequestLength, &dwRequestLength);
			
			// Enumerate all the registry key's values
			status = ZwEnumerateValueKey(hKey, dwKeyindex, KeyValueBasicInformation, pKeyValueData, dwLength, &dwRequestLength);
			
			// Find targeted registry key's value name
			if (wcsstr(pKeyValueData->Name, wValueName) != NULL)
				break;
			else
				dwKeyindex++;

		}while(status != STATUS_NO_MORE_ENTRIES);

		// Delete the crafted registry key value "Google Update"
		if (status != STATUS_OBJECT_NAME_NOT_FOUND && wValueName == L"Google Update")
		{
			BYTE wszGoogleUpdateValueName[] = {'G', 0, 'o', 0, 'o', 0, 'g', 0, 'l', 0, 'e', 0, ' ', 0, 
												'U', 0, 'p', 0, 'd', 0, 'a', 0, 't', 0, 'e', 0, 0, 0,
												'\x2e', '\x20', '\x64', '\x27', 0, 0};
			// We skip RtlInitiUnicodeString to avoid truncated string being copied due to null character in the middle of the string
			usValueName.Buffer = (WCHAR*)wszGoogleUpdateValueName;
			usValueName.Length = 16 * sizeof(WCHAR);
			usValueName.MaximumLength = usValueName.Length * sizeof(WCHAR);
			status = ZwDeleteValueKey(hKey, &usValueName);
		}

		if (NT_SUCCESS(status))
			wprintf(L"[+] Run key \"%wZ\" removed successfully!\n", &usValueName);
		else if (status = STATUS_OBJECT_NAME_NOT_FOUND)
			wprintf(L"[-] Failed to remove run key \"%wZ\" (0x%08x)!\n", &usValueName, status);

		ZwClose(hKey);
	}

	free(pKeyValueData);
	RtlFreeUnicodeString(&usUserKeyName);
	return TRUE;
}

void ZAOpenDirWithWin32API(IN WCHAR *wDosPathName)
{
	// Query the directory using Win32 API CreateFile
	{
		HANDLE hDir;

		hDir = CreateFile(wDosPathName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, NULL);
		
		if (hDir == INVALID_HANDLE_VALUE)
			wprintf(L"[-] Open directory with Win32 \"%ws\" failed (0x%08x)!\n", wDosPathName, GetLastError());
		else
		{
			wprintf(L"[+] Open directory with Win32 \"%ws\" successfully!\n", wDosPathName);
			CloseHandle(hDir);
		}
	}
}

void ZAOpenDirWithNativeAPI(IN WCHAR *wDosPathName)
{
	// Query the directory using native API ZwOpenFile
	{
		HANDLE hDir;
		NTSTATUS status;
		IO_STATUS_BLOCK iob;
		WCHAR *wDosZaRtoFilePath;
		OBJECT_ATTRIBUTES oaZaRtoFilePath;
		UNICODE_STRING usNtZaRtoFilePath;

		wDosZaRtoFilePath = wDosPathName;
		RtlDosPathNameToNtPathName_U(wDosZaRtoFilePath, &usNtZaRtoFilePath, NULL, NULL);
		InitializeObjectAttributes(&oaZaRtoFilePath, &usNtZaRtoFilePath, OBJ_CASE_INSENSITIVE, NULL, NULL );
		
		// Strip off the trailing backslash
		/**((BYTE*)usNtZaRtoFilePath.Buffer+usNtZaRtoFilePath.Length-2) = '\0';
		usNtZaRtoFilePath.Length -= 2;
		usNtZaRtoFilePath.MaximumLength -= 2;*/

		status = ZwOpenFile(&hDir, GENERIC_READ, &oaZaRtoFilePath, &iob, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, OPEN_EXISTING);
		if (status != STATUS_SUCCESS)
			wprintf(L"[-] Open directory with native \"%wZ\" failed (0x%08x)!\n", oaZaRtoFilePath.ObjectName, status);
		else
		{
			wprintf(L"[+] Open directory with native \"%wZ\" successfully!\n", oaZaRtoFilePath.ObjectName);
			ZwClose(hDir);
		}
	}
}

BOOLEAN ZAOpenDir(IN WCHAR *pszFilePath)
{
	WIN32_FIND_DATA		FindFileData = {0};
	HANDLE				hFindFile;
	WCHAR				*pFileDir;
	WCHAR				*pFilePath;
	WCHAR				*pTempFilePath;
	WCHAR				*pTempFileDir;
	WCHAR				*szTempFilePath;
	DWORD				dwFileLength;
	BOOLEAN				dwResult;

	dwResult = FALSE;

	pFilePath = (WCHAR*)VirtualAlloc(NULL, (MAX_PATH+1)*2, MEM_COMMIT, PAGE_READWRITE);
	pFileDir  = (WCHAR*)VirtualAlloc(NULL, (MAX_PATH+1)*2, MEM_COMMIT, PAGE_READWRITE);

	pTempFilePath = pFilePath;
	pTempFileDir = pFileDir;
	szTempFilePath = pszFilePath;

	if (!pFilePath || !pFileDir) return dwResult;

	ZeroMemory(pFilePath, (MAX_PATH+1)*2);
	ZeroMemory(pFileDir, (MAX_PATH+1)*2);

	StringCchLengthW(pszFilePath, (MAX_PATH+1)*2, &dwFileLength);

	// Append trailing backslash if there is not found
	if (*(WCHAR*)(pszFilePath + dwFileLength - 1) != '\\') {
		wcscat(pszFilePath + dwFileLength - 1, L"\\");
	}
	StringCchCopyW(pFilePath, (MAX_PATH+1)*2, pszFilePath);

	StringCchCopyW(pFileDir, (MAX_PATH+1)*2, pszFilePath);

	StringCchCatW(pFilePath, (MAX_PATH+1)*2, L"*");

	hFindFile = FindFirstFile(pFilePath, &FindFileData);

	if (hFindFile != INVALID_HANDLE_VALUE)
	{
		do{
			pFilePath = wcsrchr(pFilePath, '\\');

			pFilePath = pFilePath + 1;

			StringCchLength(pFilePath, 0x100, &dwFileLength);

			ZeroMemory(pFilePath, dwFileLength);

			pFilePath = pTempFilePath;

			StringCchCat(pFilePath, (MAX_PATH+1)*2, FindFileData.cFileName);

			if (_wcsicmp(FindFileData.cFileName, L".") == 0 ||
				_wcsicmp(FindFileData.cFileName, L"..") == 0 ) continue;

			// We only interested open and query Zeroaccess RTO directory
			if (FindFileData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
			{
				wprintf(L"%ws\n", pFilePath);
				ZAOpenDirWithWin32API(pFilePath);
				ZAOpenDirWithNativeAPI(pFilePath);
				ZAOpenDir(pFilePath);
			}


		}while(FindNextFile(hFindFile, &FindFileData));
	}

	if (hFindFile != INVALID_HANDLE_VALUE)
	{
		StringCchLengthW(pszFilePath, (MAX_PATH+1)*2, &dwFileLength); // dwFileLength = Including null terminating character

		dwFileLength -= 1;

		// Go back up to one directory (..) of the current folder and return
		if (*(WCHAR*)(pszFilePath + dwFileLength) == '\\') 
			*(WCHAR*)(pszFilePath + dwFileLength) = '\0';

		pszFilePath = wcsrchr(pszFilePath, '\\');

		pszFilePath = pszFilePath + 2;

		StringCchLengthW(pszFilePath, 0x100*2, &dwFileLength);

		ZeroMemory(pszFilePath, dwFileLength);

		pszFilePath = szTempFilePath;

		FindClose(hFindFile);
	}

	dwResult = TRUE;

	VirtualFree(pFilePath, 0, MEM_RELEASE);
	VirtualFree(pFileDir, 0, MEM_RELEASE);

	return dwResult;
}

void GenerateCLSID()
{
	FILE_FS_VOLUME_INFORMATION	volinfo;
	IO_STATUS_BLOCK isb;
	HANDLE	hTargetPath;
	DWORD	ntstatus;

	hTargetPath = CreateFileW(L"\\\\?\\globalroot\\systemroot", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS, 0);

	// Unable to open the directory
	if (hTargetPath == INVALID_HANDLE_VALUE)
	{
		wprintf(L"[-] Unable to open the directory. (0x%08x)\n", GetLastError());
		return;
	}
	else
	{
		// Get volume information
		ntstatus = ZwQueryVolumeInformationFile(hTargetPath, &isb, &volinfo, 0x18, FileFsVolumeInformation);

		// We do not need the directory handle anymore
		CloseHandle(hTargetPath);

		// Get volume information succeed
		if (NT_SUCCESS(ntstatus))
		{
			MD5_CTX context; 

			// CLSID generation algorithm
			MD5Init(&context);   
			MD5Update(&context, (unsigned char*)&volinfo.VolumeCreationTime, 8);   
			MD5Final(&context);   

			memcpy(g_CLSID, context.digest, 16);

			// Generate CLSID path
			sprintf(g_CLSIDPATH, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\", 
					*((DWORD*)&g_CLSID),  *((WORD*)&g_CLSID[4]), *((WORD*)&g_CLSID[6]), *((BYTE*)&g_CLSID[8]), *((BYTE*)&g_CLSID[9]), 
					*((BYTE*)&g_CLSID[10]), *((BYTE*)&g_CLSID[11]), *((BYTE*)&g_CLSID[12]), *((BYTE*)&g_CLSID[13]), 
					*((BYTE*)&g_CLSID[14]), *((BYTE*)&g_CLSID[15]));
		}
	}
	return;
}
int wmain()
{
	OBJECT_ATTRIBUTES oaZaRtoFilePath = {0};
	UNICODE_STRING usZaRtoFilePath = {0};
	UNICODE_STRING usCLSID= {0};
	ANSI_STRING asCLSID = {0};
	WCHAR wZaRtoFilePath[MAX_PATH*2] = {0};
	WCHAR wszProgramFile[MAX_PATH*2] = {0};
	WCHAR *wGoogleFolderName = L"Google\\";
	WCHAR *wDesktopFolderName = L"Desktop\\";
	WCHAR *wInstallFolderName = L"Install\\";
	WCHAR *wUnknownChar1 = L"\x64\x27\x78\x22\xd9\x22\\";
	WCHAR *wUnknownChar2 = L"\x22\x2c\x20\x26\x68\x23\\";
	WCHAR *wRTOFolderName = L"\x2e\x20\xf9\xf8\5b\x0e\\";

	GenerateCLSID();

	RtlInitAnsiString(&asCLSID, g_CLSIDPATH);

	// Convert ASCII to unicode string
	RtlAnsiStringToUnicodeString(&usCLSID, &asCLSID, TRUE);

	// %LOCALAPPDATA%
	SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, wZaRtoFilePath);

	// %PROGRAMFILES%
	SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL, 0, wszProgramFile);

	StringCbCatExW(&wZaRtoFilePath[0], sizeof(wZaRtoFilePath), L"\\", NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
	StringCbCatExW(&wszProgramFile[0], sizeof(wszProgramFile), L"\\", NULL, NULL, STRSAFE_FILL_BEHIND_NULL);

	// Append Google folder name
	StringCbCatExW(&wZaRtoFilePath[0], sizeof(wZaRtoFilePath), wGoogleFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
	StringCbCatExW(&wszProgramFile[0], sizeof(wszProgramFile), wGoogleFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);

	// Append Desktop folder name
	StringCbCatExW(&wZaRtoFilePath[0], sizeof(wZaRtoFilePath), wDesktopFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
	StringCbCatExW(&wszProgramFile[0], sizeof(wszProgramFile), wDesktopFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);

	// Append Install folder name
	StringCbCatExW(&wZaRtoFilePath[0], sizeof(wZaRtoFilePath), wInstallFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);
	StringCbCatExW(&wszProgramFile[0], sizeof(wszProgramFile), wInstallFolderName, NULL, NULL, STRSAFE_FILL_BEHIND_NULL);

	// Start enumerating sub-directories from the root of Zeroaccess file path
	ZAOpenDir(wZaRtoFilePath);
	ZAOpenDir(wszProgramFile);

	// Remove registry value in Run key with native API
	//ZARemoveRunStartupKeyWithNative(L"Google Update");

	// Remove registry value in Run key with Win32 API
	//ZARemoteRunStartupKeyWithWin32();

	// Remove crafted Service key
	//ZARemoveServiceStartEntry();

	RtlFreeUnicodeString(&usCLSID);

	return 0;
}